package pres.seanlang.utility;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;

/**
 *  反射工具
 *
 * @author SeanLang
 */
public class ReflectUtils {
    private static final String TAG = "ReflectUtils";

    /**
     *  判断Field/Method是否是static
     * @param member    被检测的对象，{@link Field}/{@link Method}/{@link Constructor}
     * @return true是静态方法或者静态属性
     */
    private static boolean isStatic(Member member) {
        int mod = member.getModifiers();
        return Modifier.isStatic(mod);
    }

    /**
     *  检测values是否符合types的要求
     * @param parameterTypes    传参类型class
     * @param parameters        传参
     * @return  true符合传参要求
     */
    private static boolean isValidAssign(Class<?>[] parameterTypes, Object[] parameters) {
        for(int i=0; i<parameterTypes.length; i++) {
            if(!parameterTypes[i].isAssignableFrom(parameters[i].getClass())) {
                return false;
            }
        }

        return true;
    }

    /**
     *  调用方法
     * @param object        class实例
     * @param method        方法
     * @param args          传参
     * @return
     */
    private static Object invoke(Object object, Method method, Object... args) {
        Object ret = null;
        if(!method.isAccessible()) { method.setAccessible(true); }
        try {
            Object instance = isStatic(method) ? null : object;
            ret = method.invoke(instance, args);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        if(!method.isAccessible()) { method.setAccessible(false); }

        return ret;
    }

    /**
     *  反射调用方法
     * @param clazz     类class,不能为空
     * @param object    对象实例,如果是静态方法的调用传null即可
     * @param name      方法名
     * @param parameterTypes   方法的传参类型
     * @param args       传参
     * @return  无参返回null
     */
    public static Object invoke(Class<?> clazz, Object object, String name, Class<?>[] parameterTypes, Object[] args) {
        while(clazz != null) {
            try {
                Method method = clazz.getDeclaredMethod(name, parameterTypes);
                return invoke(object, method, args);
            } catch (NoSuchMethodException e) {
                System.err.println("[Warning]" + TAG + "| no find method in " + clazz.getName());
                clazz = clazz.getSuperclass();
            }
        }
        return null;
    }

    /**
     *  将基础数据类型转换包装类， 如int>Integer
     * @param clazz
     * @return
     */
    private static Class<?> toWrapperClass(Class<?> clazz) {
        if(clazz.isPrimitive()) {
            //Boolean.TYPE  Character.TYPE  Byte.TYPE  Short.TYPE  Integer.TYPE  Long.TYPE  Float.TYPE  Double.TYPE  Void.TYPE
            if(clazz == Boolean.TYPE) {
                return Boolean.class;
            } else if(clazz == Character.TYPE) {
                return Character.class;
            } else if(clazz == Byte.TYPE) {
                return Byte.class;
            } else if(clazz == Short.TYPE) {
                return Short.class;
            } else if(clazz == Integer.TYPE) {
                return Integer.class;
            } else if(clazz == Long.TYPE) {
                return Long.class;
            } else if(clazz == Float.TYPE) {
                return Float.class;
            } else if(clazz == Double.TYPE) {
                return Double.class;
            } else if(clazz == Void.TYPE) {
                return Void.class;
            }
        }
        return clazz;
    }

    /**
     *  检测形参是否符合定义形参的要求。
     * @param parameters        形参
     * @param arguments         实参
     * @param start             检测的起始位置
     * @param end               检测的结束位置, 注意此处的结束位置是相对parameters或者arguments的长度,不是从start开始计算的步长.
     *                            如: parameters[0, 1, 2, 3, 4, 5, 6],要检测[4,5]的部分,则start=4,end=6.
     * @return
     */
    private static boolean isValidArgs(Class<?>[] parameters, Object[] arguments, int start, int end) {
        if((parameters.length - end) < 0 ||  (arguments.length - end) < 0) {
            return false;
        }
        for(int i=start; i<end; i++) {
            Class<?> pType = toWrapperClass(parameters[i]);
            Class<?> aType = toWrapperClass(arguments[i].getClass());
            if(!pType.isAssignableFrom(aType)) {
                return false;
            }
        }

        return true;
    }

    /**
     *  检测形参是否符合定义形参的要求。
     * @param parameters
     * @param arguments
     * @return
     */
    private static boolean isValidArgs(Class<?>[] parameters, Object[] arguments) {
        if(parameters.length != arguments.length) {
            return false;
        } else {
            return isValidArgs(parameters, arguments, 0, parameters.length);
        }
    }

    /**
     *  反射调用方法
     * @param clazz     类class,不能为空
     * @param object    对象实例,如果是静态方法的调用传null即可
     * @param name      方法名
     * @param args      传参
     * @return  无参返回null
     */
    public static Object invoke(Class<?> clazz, Object object, String name, Object... args) {
        while(clazz != null) {
            Method[] methods = clazz.getDeclaredMethods();
            for(Method method : methods) {
                if(!method.getName().equals(name)) { continue; }
                Class<?>[] parameterTypes = method.getParameterTypes();
                if(method.isVarArgs()) {    //包含可变传参
                    if(parameterTypes.length == 1) {    //只有一个可变传参
                        if(args.length == 0) {
                            return invoke(object, method);
                        } else if(args.length == 1 && args[0].getClass().isArray()) {
                            return invoke(object, method, args);
                        } else {
                            Class<?> varArgType = parameterTypes[0].getComponentType();
                            Object varArgs = Array.newInstance(varArgType, args.length);
                            for(int i=0; i<args.length; i++) {
                                if(varArgType.isAssignableFrom(args[i].getClass())) {
                                    Array.set(varArgs, i, args[i]);
                                } else {
                                    varArgs = null;
                                    break;
                                }
                            }

                            if(varArgs != null) {
                                return invoke(object, method, varArgs);
                            }
                        }
                    } else {    //末位使用可变参数
                        //检测不是可变参数的部分是否符合要求
                        if(!isValidArgs(parameterTypes, args, 0, parameterTypes.length-1)) {
                            continue;
                        }

                        if( (parameterTypes.length - args.length) == 1    //不传可变参数
                                || ((parameterTypes.length == args.length) && args[args.length-1].getClass().isArray()) ) {     //已将可变参数转换为数组
                            return invoke(object, method, args);
                        } else {
                            Object[] argArray = new Object[parameterTypes.length];
                            int varArgIndex = parameterTypes.length - 1;
                            for(int i=0; i<varArgIndex; i++) {
                                argArray[i] = args[i];
                            }
                            Class<?> varArgType = parameterTypes[varArgIndex].getComponentType();
                            Object varArgs = Array.newInstance(varArgType, args.length - varArgIndex);
                            for (int i=varArgIndex; i<args.length; i++) {
                                if(varArgType.isAssignableFrom(args[i].getClass())) {
                                    Array.set(varArgs, i-varArgIndex, args[i]);
                                } else {            //参数不一致
                                    varArgs = null;
                                    break;
                                }
                            }
                            if(varArgs != null) {
                                argArray[varArgIndex] = varArgs;
                                return invoke(object, method, argArray);
                            }
                        }
                    }
                } else {
                    if(isValidArgs(parameterTypes, args)) {
                        return invoke(object, method, args);
                    }
                }
            }

            System.err.println("[Warning]  " + TAG + "| no find [" + name +"] method in " + clazz.getName());
            clazz = clazz.getSuperclass();
        }
        return null;
    }

    /**
     *  反射调用方法。
     * @param className     反射的class名, 如: java.lang.String
     * @param name          方法名
     * @param params        传参
     * @return  无参返回null
     */
    public static Object invoke(String className, String name, Object... params) {
        try {
            Class<?> clazz = Class.forName(className);
            return invoke(clazz, null, name, params);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     *  获取属性值
     * @param clazz     反射的class
     * @param object    对象实例,如果是静态方法传null.
     * @param name      属性名
     * @return  无参返回null
     */
    public static Object get(Class<?> clazz, Object object, String name) {
        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(name);
                try {
                    if(!field.isAccessible()) { field.setAccessible(true); }
                    Object instance = isStatic(field) ? null : object;
                    return field.get(instance);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
                if(clazz == null) {
                    e.printStackTrace();
                }
            }
        }

        return null;
    }

    /**
     *  获取属性值
     * @param className     反射的class名, 如: java.lang.String
     * @param name          属性名
     * @return  返回对应属性名的值
     */
    public static Object get(String className, String name) {
        try {
            Class<?> clazz = Class.forName(className);
            return get(clazz, null, name);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     *  设置属性值
     * @param clazz     反射的class
     * @param object    对象实例,如果是静态方法传null.
     * @param name      属性名
     * @param value     新值
     */
    public static void set(Class<?> clazz, Object object, String name, Object value) {
        while(clazz != null) {
            try {
                Field field = clazz.getDeclaredField(name);
                if(!field.isAccessible()) { field.setAccessible(true); }
                Object instace = isStatic(field) ? null : object;
                try {
                    field.set(instace, value);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
                if(clazz == null) { e.printStackTrace(); }
            }
        }
    }

    /**
     *  设置属性值
     * @param className     反射的class名, 如: java.lang.String
     * @param name          属性名
     * @param value         新值
     */
    public static void set(String className, String name, Object value) {
        try {
            Class<?> clazz = Class.forName(className);
            set(clazz, null, name, value);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     *  反射实例化class
     * @param clazz             类class
     * @param parameterTypes    构造方法传参类型
     * @param parameters        构造方法传参
     * @return  类实例化
     */
    public static Object instance(Class<?> clazz, Class<?>[] parameterTypes, Object[] parameters) {
        try {
            Constructor<?> constructor = clazz.getDeclaredConstructor(parameterTypes);
            if(!constructor.isAccessible()) { constructor.setAccessible(true); }
            try {
                return constructor.newInstance(parameters);
            } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     *  反射实例化class
     * @param clazz         类class
     * @param parameters    构造方法传参
     * @return  类实例化
     */
    public static Object instance(Class<?> clazz, Object... parameters) {
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            if((parameterTypes.length == parameters.length)
                    || ((parameterTypes.length - parameters.length) == 1 && parameterTypes[parameterTypes.length - 1].isArray())) {
                try {
                    if(!constructor.isAccessible()) { constructor.setAccessible(true); }
                    return constructor.newInstance(parameters);
                } catch (InvocationTargetException | IllegalAccessException | InstantiationException e) {
                    e.printStackTrace();
                }
            }
        }

        return null;
    }

    /**
     *  反射实例化
     * @param className     反射的class名, 如: java.lang.String
     * @param parameters    构造方法传参
     * @return  类实例化
     */
    public static Object instance(String className, Object... parameters) {
        try {
            Class<?> clazz = Class.forName(className);
            return instance(clazz, parameters);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     *  创建反射包装实例
     * @param className     反射的class名, 如: java.lang.String
     * @param parameters    构造方法传参
     * @return 返回包装对象实例
     */
    public static Wrapper builder(String className, Object... parameters) {
        Class<?> clazz = null;
        Object instance = null;

        try {
            clazz = Class.forName(className);
            instance = instance(clazz, parameters);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return new Wrapper(clazz, instance);
    }

    public static class Wrapper {
        private Class<?> clazz = null;
        private Object instance = null;

        private Wrapper(Class<?> clazz, Object instance) {
            this.clazz = clazz;
            this.instance = instance;
        }

        public Object get(String name) {
            return ReflectUtils.get(clazz, instance, name);
        }

        public void set(String name, Object value) {
            ReflectUtils.set(clazz, instance, name, value);
        }

        public Object invoke(String name, Object... params) {
            return ReflectUtils.invoke(clazz, instance, name, params);
        }
    }
}
